package Enotify::Generator::ControllerCommunicator;
=head1 Enotify::Generator::ControllerCommunicator

  This package enables communication with the enotify controller 
  web application.
  
  The functionality in this package supports the communication model
  where the generator ALWAYS initiates contact with the Controller.
  The process flow is as follows
  
  1) a cron task kicks off the generator
  2) the generator asks the controller what work to do
  3) the controller responds with the name of a project
  4) the generator generates the site & sends status messages
     to the controller during the update process
  5) the generator finishes the site update, notifies the controller and exits
  
  
  the cron tab is on a 2 minute retry, with logic to keep multiple generators 
  from starting. so step 1 will happen at most 2 minutes after step 5 completes
     
  
  The type of messages the generator will send to the controller
  
  
  REQUESTS:
  * Give me a site name wito generate get_site
  * What work am I doing (supported by the controller for future implementation on the generator)
  
  Updates:
  * GENERATING SITE -> STARTED 
  * GENERATING SITE -> COMPLETE 
  * BUG LIST DOWNLOAD  -> STARTED
  * BUG LIST DOWNLOAD  -> FAILED
  * BUG LIST DOWNLOAD  -> COMPLETED
  * RENDERING PAGES  -> STARTED
  * RENDERING PAGES  -> COMPLETE
  * RENDERING GRAPHS -> STARTED
  * RENDERING GRAPHS -> COMPLETE
     

   The controller identifies all requests based on the IP address of the 
   generator which is passed with every call. 
   
   
   Deployment Requirement: 
     Because it is technally possible for a DHCP generator to change it's IP 
     address while running a site, the enterprise deployment of this mechanism 
     requires generators with Static IP addresses.
     
     
   Example use:
   
    $controller_comunicator = Enotify::Generator::ControllerCommunicator::new();
     
    $site_name = $controller_comunicator->get_site_to_generate()
    
    print "Site Name " . $site_name;
    
    >Site Name foo
    
    $controller_comunicator->
     
=cut

use strict;
use warnings;


#use CCBU::Utils::SimpleLog qw (:all);
use LWP::Simple;   ## needed for HTTP access
use LWP::UserAgent; ## needed for HTTP authentication access
use HTTP::Request::Common;
use IO::Socket::INET;
use Sys::Hostname;

use XML::Simple; # needed for parsing responses fromt the controller
use Data::Dumper;




sub new 
{
    my ( $test_mode ) = @_;
    
    if (!defined($test_mode) )
    {
        print("YOU NEED TO DEFINE TEST MODE. new(1) means test mode, new(0) means production\n";
        return 0;
    }
    
    my $self = {};
    $self->{IP_ADDRESS} = lookup_local_ip_address();
    $self->{HOSTNAME} = hostname();
    $self->{GEN_PATH} = 'generation.xml';
    $self->{TEST_MODE} = $test_mode;
    $self->{USER_AGENT} = LWP::UserAgent->new;
    $self->{USER_AGENT}->timeout(10);
    
    bless $self;
    
    return $self;
}  

=head2 ControllerCommunicator->get_site_to_generate() {{{

    Purpose: This object encapsulates the details of
             a bug ownership change. This object will 
             be used inside a bug in order to change the
             owner of that bug.
             
    Expects: Nothing. Seriously, it is the controllers responcibility
             to deal with generators that go crazy or ask for this 
             info multiple times.
                  
    Returns: (STRING) The short name of the site that is being updated. This
             is also the last directory in the path to the 
             directory for the site.
             
             "enotify/sites/" . $controller_comunicator->get_site_to_generate()
    
             > enotif/sites/foo
=cut
sub get_site_to_generate
{
    my ($self) = @_;
    
    my $result = $self->make_controller_post('generations.xml');
    
    if ($result == 0)
    {
        return 0;   
    }
    else
    {
        # parse the response for the short name
        my $response_XML_reference = XMLin($result);
        my $xml_as_hash = Dumper($response_XML_reference);
        
        # FIXME: need to find the hash when I can see the XML
        # http://search.cpan.org/~grantm/XML-Simple-2.18/lib/XML/Simple.pm
        return $xml_as_hash; 
    }
    
    
} # }}}

sub update_generation_started
{
    my ($self) = @_;
    $self->make_controller_put($self->get_gp(), "Started generating the site");
}

sub update_generation_completed
{
    my ($self) = @_;
    $self->make_controller_put($self->get_gp(), "Completed generating the site");    
}


sub update_buglist_download_start
{
    my ($self) = @_;
    $self->make_controller_put($self->get_gp(), "started buglist download");    
}




sub get_gp
{
    my ($self) = @_;
    return $self->{GEN_PATH};
}

=head2 lookup_local_ip_address  {{{

    Purpose: get the local ip addres
    
    Returns: (STRING) the ip address.
    
    Example: "192.168.1.1"

=cut
sub lookup_local_ip_address
{
    my ($self) = @_;
    my $sock = IO::Socket::INET->new(
                           PeerAddr=> "example.com",
                           PeerPort=> 80,
                           Proto   => "tcp");
    return $sock->sockhost;
    
}  # }}}




=head2 $self->make_controller_post() {{{

    Purpose: (INTERNAL ONLY) This method encapulates the mechanics
             of calling the controller with a post a.k.a create message
             
    Expects: The request path to call on the controller. 
             This object already knows the base URL for the controller, so 
             you only need to pass the second half of the URL.
             
             For example: if the URL to get a new site to generate is 
                             http://0.0.0.0:3000/generations.xml
                          then you should call
                          $self->make_controller_post("generations.xml");
                  
    Returns: (STRING) OR (FALSE/0)
    
             If the request to the controller fails this method returs false (0)
             Otherwise it returns the body of the response message.
             This is usually the XML returned by the REST request to the 
             controller.
                              

=cut
sub make_controller_post
{
    my ($self, $request_path) = @_;
    
    my $response = $self->make_controller_request($request_path, POST);
    
    if ($response->is_success) 
    {
        return $response->decoded_content;  
    }
    else 
    {
        return 0;
    }                                                               
              
    
} # }}}

=head2 $self->make_controller_put($request_path, $params) {{{

    Purpose: (INTERNAL ONLY) This method encapulates the mechanics
             of calling the controller with a put a.k.a create message
             
    Expects: The request path to call on the controller. 
             This object already knows the base URL for the controller, so 
             you only need to pass the second half of the URL.
             
             For example: if the URL to get a new site to generate is 
                             http://0.0.0.0:3000/generations.xml
                          then you should call
                          $self->make_controller_post("generations.xml");
                  
    Returns: (boolean) 
             true: If the request update works
             false (a.k.a 0 zero): if the update fails
                              

=cut
sub make_controller_put
{
    my ($self, $request_path, $params) = @_;
    
    my $response = $self->make_controller_request($request_path, PUT, $params);
    
    if ($response->is_success) 
    {
        return $response->decoded_content;  
    }
    else 
    {
        return 0;
    }                                                               
              
    
} # }}}

=head2 $self->make_controller_request($request_path, $method, $params) {{{

    Purpose: (INTERNAL ONLY) This method encapulates the mechanics
             of calling the controller with a put a.k.a create message
             
    Expects: 
             (STRING) The request path to call on the controller. 
             This object already knows the base URL for the controller, so 
             you only need to pass the second half of the URL.
             
             For example: if the URL to get a new site to generate is 
                             http://0.0.0.0:3000/generations.xml
                          then you should call
                          $self->make_controller_request("generations.xml", PUT);
                  
              (METHOD) PUT or POST
                          
                          
    Returns: (boolean) 
             true: If the request update works
             false a.k.a 0 zero: if the update fails
                              

=cut
sub make_controller_request
{
    my ($self, $request_path, $method, $params) = @_;   
    
    my $base_params = [ generator_ip   => $self->{IP_ADDRESS},
                        generator_name => $self->{HOSTNAME} ];
   
    ### combine the provided params with the default IP and hostname params                        
    my $complete_params; 
    if (!defined($params))
    {
       $complete_params = $base_params;
    }
    else
    {
       $complete_params = [@$params, @$base_params];
    }
    
    my $ua = LWP::UserAgent->new;
    $ua->timeout(10);
    
    ## call out to the webserver
    if ($self->{TEST_MODE})
    {
        ## ok just pretend to call out to the server   
        
        my $result = "Calling: " . $method . ' http://0.0.0.0:3000/' . $request_path . "\n";
        $result .= @$complete_params;
        
        print $result;
        return '<result>200</result>';
    }
    else
    {
        ## this is the real thing start calling people!!!   
        
        return $response = $self->{USER_AGENT}->request(PUT 'http://0.0.0.0:3000/'
                                                         . $request_path,
                                                         $complete_params);
        if ($response->is_success) 
        {
           return $response->decoded_content;  
        }
        else 
        {
           return 0;
        }          
    
    }
    
   
    

    #my $ua = LWP::UserAgent->new;
    #$ua->timeout(10);
    #return $response = $ua->request(PUT 'http://0.0.0.0:3000/' . $request_path,
    #                                    $complete_params);
    
    #                            [ generator_ip   => $self->get_local_ip_address,
    #                              generator_name => $self->get_local_host_name,
    #                              message        => $message,
    #                              bug_list       => $bug_list]);

                                  
                                  
                                  
                                  
#(4:04:58 PM) Chris Warren: [@$ref1, @$ref2] would be one way.
#16:05
#(4:05:14 PM) Chris Warren: That would create a new arrayref without damaging the existing ones.
#(4:05:32 PM) Paul Russell: so $ref1 = [ prop1   => "foo", prop2 => "bar"]  
#[(@$ref1, @$ref2)]  [ prop1   => "foo", prop2 => "bar"]

}  # }}}


1;
